package main.java.day11;

import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
 * 将聊天室服务端内容独立完成一次
 * 下面内容可以选做:
 * 配合客户端实现支持昵称与私聊功能
 * @author Xiloer
 *
 */
public class Server {
    private ServerSocket server;
    //	private List<PrintWriter> allOut;
    private Map<String, PrintWriter> allOut;
    public Server(){
        try {
            server=new ServerSocket(8088);
            allOut = new HashMap<String, PrintWriter>();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
    private void addOut(String key,PrintWriter value)
    {
        synchronized(this)
        {
            allOut.put(key, value);
        }
    }

    private synchronized void removeOut(String  key)
    {
        allOut.remove(key);
        System.out.println("当前在线人数为："+ allOut.size());
    }
    private synchronized void sendMsgToAll(String message)
    {
        for(PrintWriter out: allOut.values())
        {
            out.println(message);
            System.out.println("当前在线人数为："+ allOut.size());
        }
    }


    private synchronized void sendMsgToPrivate(String nickname,String message)
    {
        PrintWriter pw = allOut.get(nickname);
        if(pw!=null)
        {
            pw.println(message);
            System.out.println("aaa");
            System.out.println("当前在线私聊人数为："+ allOut.size());
        }
    }


    public void start(){
        try{

            while(true){
                System.out.println("等待客户端连接.......");
                Socket socket=server.accept();
                System.out.println("客户端已连接！");
                ClientHandler client=new ClientHandler(socket);
                Thread t=new Thread(client);
                t.start();
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        Server server=new Server();
        server.start();
    }
    private class ClientHandler implements Runnable{
        private Socket socket;
        private String host;
        private String nickName;
        public ClientHandler(Socket socket){
            this.socket=socket;
            InetAddress adress=socket.getInetAddress();
            host=adress.getHostAddress();
        }
        private String getNickName() throws Exception
        {
            try
            {
                //服务端的输入流读取客户端发送来的昵称输出流
                InputStream in = socket.getInputStream();
                InputStreamReader isr =
                        new InputStreamReader(in,"UTF-8");
                BufferedReader bReader = new BufferedReader(isr);
                //服务端将昵称验证结果通过自身的输出流发送给客户端
                OutputStream out = socket.getOutputStream();
                OutputStreamWriter iosw =
                        new OutputStreamWriter(out,"UTF-8");
                PrintWriter ipw = new PrintWriter(iosw,true);
                //读取客户端发来的昵称
                String nameString = bReader.readLine();
                while(true)
                {
                    if(nameString.trim().length()==0)
                    {
                        ipw.println("FAIL");
                    }
                    if(allOut.containsKey(nameString))
                    {
                        ipw.println("FAIL");
                    }
                    else
                    {
                        ipw.println("OK");
                        return nameString;
                    }
                }
            }
            catch(Exception e)
            {
                throw e;
            }
        }

        public void run()
        {
            PrintWriter pw = null;
            try
            {
                /*
                 * 通过客户端的Socket获取客户端的输出流
                 * 用来将消息发送给客户端
                 */
                OutputStream os = socket.getOutputStream();
                OutputStreamWriter osw = new OutputStreamWriter(os,"UTF-8");
                pw = new PrintWriter(osw,true);

                /*
                 * 将客户昵称和其所说的话作为元素存入共享集合HashMap中
                 */
                nickName = getNickName();
                addOut(nickName, pw);
                Thread.sleep(100);

                /*
                 * 服务端通知所有客户端，某用户登录
                 */
                sendMsgToAll("[系统通知]：欢迎**"+nickName+"**登陆聊天室!");

                /*
                 * 通过客户端的Socket获取输入流
                 * 读取客户端发送来的信息
                 */
                InputStream is = socket.getInputStream();
                InputStreamReader isr = new InputStreamReader(is,"UTF-8");
                BufferedReader br = new BufferedReader(isr);
                String msgString = null;

                while((msgString = br.readLine())!=null)
                {
                    //验证是否是私聊
                    if(msgString.startsWith("@"))
                    {
                        /*
                         * 私聊格式：@昵称：内容
                         */
                        int index = msgString.indexOf(":");
                        if(index >=0)
                        {
                            //获取昵称
                            String name = msgString.substring(1,index);
                            String info = msgString.substring(index+1,msgString.length());
                            info = nickName + "对你说："+ info;
                            //将私聊信息发送出去
                            sendMsgToPrivate(name, info);
                            //服务端不在广播私聊的信息
                            continue;
                        }
                        System.out.println("已输出");
                    }
                    /*
                     * 遍历所有输出流，将该客户端发送的信息转发给所有客户端
                     */
                    System.out.println(nickName+"说："+ msgString);
                    sendMsgToAll(nickName+"说："+ msgString);

                }
            }
            catch (Exception e)
            {
                /*
                 * 因为Win系统用户的客户端断开连接后，br.readLine()方法读取
                 * 不到信息就会抛出异常，而Linux系统会持续发送null；
                 * 因此这里就不在将捕获的异常抛出了。
                 */
            }
            finally
            {
                /*
                 * 当执行到此处时，说明客户端已经与服务端断开连接
                 * 则将该客户端存在共享集合中的输出流删除
                 */
                removeOut(nickName);
                /*
                 * 通知所有客户端，某某客户已经下线
                 */
                sendMsgToAll("[系统通知]："+nickName + "已经下线了。");

                /*
                 * 关闭socket，则通过Socket获取的输入输出流也一同关闭了
                 */
                if(socket!=null)
                {
                    try
                    {
                        socket.close();
                    }
                    catch(IOException e)
                    {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

}




